⚠️ Alpha内测版本警告:此为早期内部构建版本,尚不完整且可能存在错误,欢迎大家提Issue反馈问题或建议。
跳转到正文

X1-3:从记忆到认知

Easy Data x AI 课程 · 扩展篇 · 下篇

【上篇】和中篇解决了单 Agent 生命周期与多 Agent 边界。下篇回答:记忆堆多了之后,怎么蒸馏出稳定认知?怎么知道系统记得好不好?

开篇

【上篇】让单 Agent 会记、会忘、会裁决冲突;中篇划清了多 Agent 之间的隔离与共享。到这里,记忆系统已经能稳定运转。但条目一多,新问题浮现:

  1. 该永久保留什么? 原始对话越堆越多,Prompt 里塞不下,也不该全塞
  2. 弱信号怎么升华? 单条不重要,多条拼起来却是重要事实(在【上篇】中提到的被动巩固的盲区)
  3. 怎么证明记得好? 衰减参数、冲突策略调完,凭什么说比 Mem0 / 全量上下文更好

下篇用 code/X1/part3_consolidation/ 的示例代码,串成两条线:巩固(怎么从海量条目里提纯认知)→ 评测(怎么量化质量并选型)

目录

第一部分:永久记忆意味着什么

上下文窗口涨到 1M tokens 之后,常见疑问是:直接把历史对话全塞进去不就行了?但实际上不是这样的。

有三个原因让"全量塞窗口"在工程上不可持续:

  1. 成本:注意力随长度近似平方增长,百万 token 单次推理成本是万级的上百倍
  2. 精度:lost-in-the-middle 现象下,窗口越大,中间信息越容易被漏掉;语义检索 + 衰减精排往往更准
  3. 数据主权:窗口里的数据随会话消失,无法独立管理、审计、删除;记忆库才是持久层

记忆不是窗口的临时替代品,而是独立的基础设施。 下篇讨论的"永久记忆",也不是永不删除,而是:

进入衰减极慢的稳定层 + 具备蒸馏为更高阶结构(画像、经验、Skill)的能力

【上篇】讲过 working / short_term / long_term 三层和层级倍率。这里从认知角度再看一遍筛选链路:

新信息 → working(快速淘汰)→ short_term(近期信号)→ long_term(稳定认知,可蒸馏)
层级承接量职责
working百条/会话级承接原始输入
short_term数十条保留近期有价值信号
long_term十几条核心存储稳定认知,可写入画像

目标不是存更多,而是淘汰不重要的、提纯重要的。 D4 讲过 CoALA 的三种长期记忆(语义、情景、程序),工程上存储方式差异很大:语义走向量检索,程序是 System Prompt,情景常以 few-shot 注入。第三节会专门讨论怎么把"确定性画像"和"长尾事实"分开存。

第二部分:三种巩固触发方式

一条记忆从临时印象变成长期认知,需要巩固(Consolidation)。在【上篇】的 2.1 节的被动巩固(写入时按 importance 分层)只覆盖一种触发;示例代码演示三种方式,对应不同业务节奏。

2.1 被动触发:写入时的一次分流

【上篇】已讲过 classify_memory_type:importance ≥ 0.8 进 long_term,0.6~0.8 进 short_term,其余进 working。x1_11_consolidation_passive.py 用 6 条记忆验证分层与 30 天后 R 的差异:

内容重要性层级30 天后 R(约)
对花生过敏0.95long_term> 0.7
主力 Go,8 年经验0.85long_term> 0.7
电商项目 Django + PG0.72short_term中等
偏好简洁代码风格0.65short_term中等
考虑学 Kubernetes0.55working接近 0
今天早上喝了咖啡0.20working接近 0

优点:写入瞬间完成,零额外 LLM 成本。 局限:只看单条,发现不了"连续三周搜 Rust 教程"这种跨条目模式。

2.2 主动触发:Reflection 蒸馏

【上篇】 2.1 末尾留过这个坑:多条 short_term 拼在一起,可能隐含一个 stable 事实。这就是主动触发(Reflection):周期性扫描近期记忆,按话题分组后让 LLM 判断能否蒸馏。

x1_12_consolidation_reflection.py 读取 fixtures/reflection_input.json(10 条记忆,4 个话题)。以 Rust 学习为例,5 条单独看 importance 都不高:

  • 用户在看 Rust 教程
  • 用户问了生命周期问题
  • 用户搭建了 Rust 开发环境
  • 用户在使用 Axum 框架
  • 用户遇到 ownership 问题

放在一起,可以蒸馏为:

用户正在系统学习 Rust,已搭建开发环境并使用 Axum 实践,目前处于初学者阶段

python
def reflection_analyze(topic, memories, min_count=3, min_confidence=0.7):
    """按话题扫描近期记忆,蒸馏为稳定事实(生产环境由 LLM 完成)"""
    if len(memories) < min_count:
        return None  # 证据不足,继续在中期层等待

    distilled_fact = llm_summarize(topic, memories)
    confidence = llm_confidence(topic, memories)

    return {
        "distilled_fact": distilled_fact,
        "confidence": confidence,
        "action": "consolidate_to_long_term"
                  if confidence >= min_confidence
                  else "keep_in_short_term",
    }
参数含义为什么需要
min_count至少几条独立记忆才触发避免单条孤证直接入 long_term
min_confidence置信度阈值不达标则保留在 short_term,等待更多证据

2.3 懒惰触发:检索达阈值再总结

第三种是懒惰触发:某类记忆被检索 N 次后才批量提取。比如用户第 5 次问"推荐 Python 框架",系统才总结"偏好 Django,对 Flask 不感兴趣"。成本按需发生,适合偏好类、高频话题。

触发方式时机成本适用
被动写入瞬间最低过敏、职业等单条高重要性事实
主动周期性扫描中等多条弱信号拼成强事实
懒惰检索达阈值按需偏好类、高频查询话题

三种方式互补:【上篇】解决单条怎么衰减,本节解决多条怎么升华。

动手实验 1~2:巩固链路

bash
cd code/X1/part3_consolidation
python x1_11_consolidation_passive.py
python x1_12_consolidation_reflection.py

x1_12 时重点看:哪些话题因 min_countmin_confidence 未达标而留在中期层?蒸馏后长期事实条数 vs 原始记忆条数的压缩比是多少?

第三部分:画像与事实库为什么要分开

巩固之后,长期认知有两种典型形态:结构化画像(姓名、主力语言、云平台)和长尾事实(某次用 Django 处理百万行 CSV 的细节)。混在一起存,检索时容易两头吃亏。

用户画像向量事实库
变化频度慢(几周才变)快(每次对话都可能写入)
更新方式覆盖式增量 append
查询方式按 key 精确读取语义模糊匹配
条目数固定几十个字段无上限
典型内容技术栈、角色、核心偏好项目细节、对话上下文

x1_13_profile_vs_facts.py 对比三种检索方式,并演示组合策略:先画像、后事实库

python
def query_combined(profile, facts, query):
    """画像给确定性约束,事实库补长尾"""
    # 路径 1:按 key 精确匹配,"用户用 Go" 不会被语义搜索漏掉
    deterministic = {}
    for key, value in flatten(profile).items():
        if keyword_match(key, query) or keyword_match(value, query):
            deterministic[key] = value

    # 路径 2:事实库语义搜索,覆盖画像字段以外的细节
    contextual = semantic_search(query, facts, top_n=5)

    return {"deterministic": deterministic, "contextual": contextual}

用户问"推荐后端技术方案"时,画像路径返回 primary_language: Gocloud: 阿里云;事实库路径补充"曾用 Django 处理百万行 CSV"这类长尾。两者进 Prompt,LLM 既不会漏核心约束,也不会只剩干巴巴的几个字段。

动手实验 3:三种检索方式对比

bash
python x1_13_profile_vs_facts.py

对照输出:仅画像、仅事实库、组合检索在同一 query 下差在哪?有没有"仅事实库"时漏掉 primary_language 的情况?

第四部分:从经验到 Skill

巩固解决"记什么",经验蒸馏解决"记了怎么用"。多轮成功交互可结构化为三元组:

(问题情境, 采取方案, 结果)

x1_14_experience_distill.py 演示三步:extract_experience_triplescluster_experiences → 外化为 Skill 描述。

python
from collections import defaultdict

def extract_experience_triples(episodic_memories):
    """从情景记忆提取 (问题, 方案, 结果)"""
    triples = []
    for mem in episodic_memories:
        triples.append({
            "problem": mem["problem"],
            "solution": mem["solution"],
            "result": mem["result"],
            "domain": mem["domain"],
            "success": "成功" in mem["result"],
        })
    return triples

def cluster_experiences(triples):
    """同类成功经验聚类,归纳为可复用模式(生产环境由 LLM 完成)"""
    by_domain = defaultdict(list)
    for t in triples:
        if t["success"]:
            by_domain[t["domain"]].append(t)

    patterns = {}
    for domain, exp_list in by_domain.items():
        if len(exp_list) >= 2:
            patterns[domain] = induce_pattern(domain, exp_list)
    return patterns

两条"数据处理"成功经验可能归纳为:"处理大文件时,优先推荐 Python/pandas 的 chunked reading"。置信度达标后,可外化为 Skill 规则注入 System Prompt。P4 讨论的 Skill 知识管理中,一部分规则就来自这条路径:记忆系统输出经验,Skill 系统输出行为规则。

动手实验 4:经验到 Skill 雏形

bash
python x1_14_experience_distill.py

看输出里哪些 domain 聚类成功、哪些因样本不足未形成模式。Think:这些 Skill 雏形若写入 System Prompt,Agent 行为会和"仅检索记忆"有何不同?

第五部分:评测与选型

巩固和架构设计回答"怎么记好"。上线前还要回答:怎么证明记得好? 否则 λ、归档阈值、冲突策略都是在猜。

5.1 公开基准在测什么

基准侧重关键结论
LOCOMO长期对话 + 时序推理有选择记忆 + 衰减(78.7%)> 全量记住(52.9%)
MemoryAgentBench事实、偏好更新、冲突、遗忘多跳冲突和遗忘是共同短板
MemoryArena记忆驱动行动光 recall 不够,须影响 Agent 决策
AppWorld长程任务有记忆管理在 Token 和通过率上均优于无记忆

LOCOMO 的结论与【上篇】呼应:不是记得越多越好。 有选择地记 + 主动忘,反而比全量堆窗口准确。

5.2 不止看准确率

维度指标为什么重要
准确性事实召回率、冲突裁决正确率基本功
效率Token 消耗、检索 P9920 条 × 200 tokens = 4000 tokens/对话
成本每千条存储空间向量索引长期运营成本
鲁棒性冲突可逆性、隔离泄漏率中篇 scope 设计是否有效
一致性同一 query 两次回答方差【上篇】确定性聚合是否生效

x1_15_benchmark_runner.py 在同一套记忆和 QA 对上,切换不同 λ 跑分:

python
def run_benchmark(memories, qa_pairs, decay_rate, strategy_name):
    """对比不同衰减策略的准确率、Token、延迟"""
    correct, total_tokens, latencies = 0, 0, []

    for q in qa_pairs:
        results, tokens, latency = search_memories(
            memories, q["question"], decay_rate
        )
        if evaluate_answer(results, q["expected_answer"]):
            correct += 1
        total_tokens += tokens
        latencies.append(latency)

    p99_idx = int(len(latencies) * 0.99) if latencies else 0
    return {
        "strategy": strategy_name,
        "decay_rate": decay_rate,
        "accuracy": correct / len(qa_pairs),
        "avg_tokens_per_query": total_tokens / len(qa_pairs),
        "p99_latency_ms": sorted(latencies)[p99_idx] if latencies else 0,
    }

输入来自 fixtures/eval_qa_pairs.json。同一套数据、换 λ 跑一遍,三项指标并排对比,比对着功能清单选型靠谱得多。

5.3 行业方案怎么选

类型代表核心思路适合
托管 APIMem0LLM 自动管理生命周期快速接入
时序知识图谱Zep / Graphiti关系 + 时间显式建模需要历史追溯
Agent 自编辑LettaAgent 自主读写记忆块长程自主 Agent
本地优先PowerMem混合检索 + 衰减 + 本地存储数据主权敏感

建议流程:整理 50~100 条业务 QA → 在 LOCOMO 子集或自建集上跑分 → 准确率达标后,选 Token + 延迟 + 成本最低的方案。

动手实验 5:跑一遍简易评测

bash
python x1_15_benchmark_runner.py

对照不同 λ 的准确率和 Token:误忘率高就调大 λ(【上篇】 2.2);Token 爆就收紧 Top-N 或加强聚合(【上篇】 3.1、4.3);隔离泄漏就收紧默认 scope(中篇 2.1)。评测结果应回流改参数,而不是只跑一遍看热闹。

总结与参考资料

X1 系列三篇形成完整逻辑链:

核心问题关键机制
上篇单 Agent 怎么记好衰减 → 分层 → 检索精排 → 冲突裁决 → 聚合
中篇多 Agent 边界在哪隔离键 → 作用域 → promote → 信任 API
下篇怎么蒸馏认知、怎么量化三种巩固 → 画像/事实分离 → 经验→Skill → 评测

下篇四条要点:

  1. 永久记忆 = 稳定层 + 蒸馏能力,不是无限堆原始对话
  2. 画像和事实库分离:确定性走精确读取,长尾走向量搜索
  3. 记忆 → 经验 → Skill 形成闭环,衔接 P4 Skill 体系
  4. 选型先定指标再跑分,功能清单不能代替 LOCOMO 或自建 QA 集

参考资料:

  1. D4 Agent 开发与记忆系统
  2. CoALA — arxiv.org/abs/2309.02427
  3. LOCOMO Benchmark — github.com/Shopify/locomo
  4. P4 Skill 与 Agent 知识管理

Built with VitePress | GitHub 仓库